<?php

namespace Service\RpcService;
use Service\BaseService;
use Service\Code\Normal;

/**
 * socket基类服务中心
 * @author chenshubo
 */
class BaseSocketClientService extends BaseService
{
    const READ_LENGTH = 16 * 1024;
    const SOCKET_ERROR = 100000;
    protected $serviceName;
    protected $service_ip = null;
    protected $service_port = null;
    
    /**
     * 开启socket实例
     * @param array $argments
     * @return mixed
     */
    public function openSocketInstance( array $argments = [] )
    {
        set_time_limit(0);
        ini_set('memory_limit', -1);
        
        //默认连接
        $local_config['local_host'] = env($this->service_ip);
        $local_config['local_port'] = env($this->service_port);
        if( $local_config['local_host'] && $local_config['local_port']) {
            $ip     = $local_config['local_host'];
            $port   = $local_config['local_port'];
        } else {
            $res = $this->getAvailableServiceFromConsulDisCovery();
            if( $res['code'] != Normal::SUC ) {
                return $res;
            }
            $ip     = $res['data']['Address'];
            $port   = $res['data']['Port'];
        }
        
        //判断系统环境
        $php_os = strtoupper(substr(PHP_OS,0,3));
        if( $php_os != 'WIN' ) {
            //向服务端发送数据
            $buffer = base64_encode(rawurlencode(json_encode($argments,JSON_UNESCAPED_UNICODE)))."\n";
            $result = $this->swooleClientRead($ip, $port, $buffer);
            return $result;
        }
        
        // 创建socket
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        
        if ($socket <= 0) {
            $reason = socket_strerror($socket);
            return Normal::returnCode(self::SOCKET_ERROR,"Msg=>【服务准备中，请稍等片刻！】,Code=>2,Errors=>创建socket失败,REASON【{$reason}】");
        }
        
        try {
        
            // 连接服务器
            $result = socket_connect($socket,$ip,$port);
            if ($result < 0 || is_null($result) || !$result) {
                $reason = socket_strerror($result);
                return Normal::returnCode(self::SOCKET_ERROR,"Msg=>【服务准备中，请稍等片刻！】,Code=>3,Errors=>连接失败,REASON【{$reason}】");
            }
        
            //设置非阻塞读取
            socket_set_nonblock($socket);
            
            //向服务端发送数据
            $buffer = base64_encode(rawurlencode(json_encode($argments,JSON_UNESCAPED_UNICODE)))."\n";
            
            // 写入文件信息
            if (!socket_write($socket, $buffer, strlen($buffer)))  {
                $reason = socket_strerror($socket);
                return Normal::returnCode(self::SOCKET_ERROR,"Msg=>【服务准备中，请稍等片刻！】,Code=>4,Errors=>消息发送失败,REASON【{$reason}】");
            }
            
            // 读取socket返回的数据
            return $this->socketRead($socket);
        } catch (\Exception $e) {
            // 关闭socket
            @socket_close($socket);
            return Normal::returnCode(self::SOCKET_ERROR,"Msg=>【服务准备中，请稍等片刻！】,Code=>5,Errors=>" . $e->getMessage());
        }
    }

    /**
     * 通过swoole client 发送 stream流
     * @param string $host
     * @param string $port
     * @param string $msg
     */
    public function swooleClientRead(string $host,string $port,string $msg)
    {
        try
        {
            $client = new \Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
            if ( !$client->connect($host,(int)$port,3)) {
                return Normal::returnCode(Normal::FAIL,"connect failed [{$host}:{$port}]. Error: {$client->errCode}");
            }
            
            $client->send($msg);
            $response = null;
            while ( true )
            {
                $response .= $client->recv();
                if( strpos($response,"\n") !== false ) {//代表tcp client 处理结束,否则继续读取
                    $lastMsg = \explode("\n", $response);
                    $response = current($lastMsg);
                    break;
                }
            }
        
            $client->close();
            return @json_decode(rawurldecode(base64_decode($response)),true);
        }
        catch ( \Throwable $th )
        {
            return Normal::returnCode(Normal::FAIL,"connect failed [{$host}:{$port}]. Error: {$th->getMessage()}");
        }
    }
    
    /**
     * 读取socket数据.
     * @param $socket
     * @return mixed
     */
    public function socketRead($socket)
    {
        $response = null;
        while ( true ) 
        {
            $response .= socket_read ($socket,self::READ_LENGTH);
            if( strpos($response,"\n") !== false ) {//代表tcp client 处理结束,否则继续读取
                $lastMsg = \explode("\n", $response);
                $response = current($lastMsg);
                break;
            }
        }
        @socket_close($socket);
        return @json_decode(rawurldecode(base64_decode($response)),true);
    }
}